package pod

import (
	"context"
	"encoding/json"
	"fmt"
	"krm-backend/controllers"
	"krm-backend/utils/logs"
	"net/http"

	"github.com/dotbalo/kubeutils/kubeutils"
	"github.com/gin-gonic/gin"

	"github.com/gorilla/websocket"
	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes/scheme"

	"k8s.io/client-go/tools/clientcmd"

	"k8s.io/client-go/tools/remotecommand"
)

// 封装一个消息的结构体
type WsMessage struct {
	// 定义消息类型，执行命令还是调整大小
	MsgType string `json:"msgType"`
	//用来表示执行的命令是什么
	Data string `json:"data"`
	//调整窗口大小的数值
	Rows uint16 `json:"rows"`
	Cols uint16 `json:"cols"`
}

// 定义一个结构体，实现streamOptions接口
type ptyHandler struct {
	ws         *websocket.Conn
	resizeChan chan remotecommand.TerminalSize
}

// 定义Read方法实现io.Reader接口
func (pty *ptyHandler) Read(p []byte) (int, error) {
	//获取ws数据
	_, msg, err := pty.ws.ReadMessage()
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "读取WebSocket数据失败")
		return 0, err
	}

	logs.Debug(map[string]interface{}{"raw_message": string(msg)}, "收到WebSocket原始消息")

	// 2.判断是执行命令还是调整大小
	//解析消息
	var wsMessage WsMessage
	if err := json.Unmarshal(msg, &wsMessage); err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error(), "raw_data": string(msg)}, "解析JSON数据失败")
		// 不要直接返回错误，尝试直接使用原始数据
		logs.Debug(map[string]interface{}{"fallback_data": string(msg)}, "使用原始数据作为命令")
		wsDataByte := []byte(string(msg))
		n := copy(p, wsDataByte)
		return n, nil
	}

	// 判断是否调整窗口大小
	if wsMessage.MsgType == "resize" {
		logs.Debug(map[string]interface{}{"rows": wsMessage.Rows, "cols": wsMessage.Cols}, "调整窗口大小")
		var size remotecommand.TerminalSize
		size.Height = wsMessage.Rows
		size.Width = wsMessage.Cols
		//扔到调整窗口大小的队列
		select {
		case pty.resizeChan <- size:
			// 成功发送
		default:
			// channel满了，忽略这次resize
			logs.Debug(nil, "resize channel满了，忽略此次调整")
		}
		return 0, nil
	} else {
		//执行命令
		logs.Debug(map[string]interface{}{"command": wsMessage.Data}, "执行命令")
		wsDataByte := []byte(wsMessage.Data)
		n := copy(p, wsDataByte)
		return n, nil
	}
}

// 定义Write方法实现io.Writer接口
func (pty *ptyHandler) Write(p []byte) (int, error) {
	logs.Debug(map[string]interface{}{"data": string(p), "length": len(p)}, "命令执行结果")

	// 检查WebSocket连接状态
	if pty.ws == nil {
		logs.Error(nil, "WebSocket连接为nil")
		return 0, fmt.Errorf("WebSocket连接为nil")
	}

	returnDataByte := make([]byte, len(p))
	n := copy(returnDataByte, p)

	err := pty.ws.WriteMessage(websocket.TextMessage, returnDataByte)
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "写入WebSocket数据失败")
		return 0, err
	}

	logs.Debug(map[string]interface{}{"bytes_written": n}, "成功写入WebSocket")
	return n, nil
}

// 实现TerminalSizeQueue接口
func (pty *ptyHandler) Next() *remotecommand.TerminalSize {
	select {
	case size := <-pty.resizeChan:
		logs.Debug(map[string]interface{}{"height": size.Height, "width": size.Width}, "返回新的终端尺寸")
		return &size
	default:
		// 非阻塞，返回nil表示没有新的尺寸
		return nil
	}
}

// 发送错误消息到前端
func (pty *ptyHandler) sendError(errMsg string) {
	if pty.ws != nil {
		errorMsg := fmt.Sprintf("\r\n❌ 错误: %s\r\n", errMsg)
		pty.ws.WriteMessage(websocket.TextMessage, []byte(errorMsg))
	}
}

func Exec(r *gin.Context) {
	logs.Debug(nil, "开始执行命令")

	var info controllers.Info
	kubeconfig := controllers.NewInfo(r, &info, "执行成功")
	clientset, err := kubeutils.NewClientSet(kubeconfig, 0)
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "clientset创建失败")
		info.ReturnData.Message = "创建ClientSet失败:" + err.Error()
		info.ReturnData.Status = 400
		r.JSON(http.StatusOK, info.ReturnData)
		return
	}

	// 设置默认命令，优先使用sh
	if info.DefaultCommand == "" {
		info.DefaultCommand = "/bin/sh"
	}

	logs.Debug(map[string]interface{}{
		"pod":       info.Name,
		"namespace": info.Namespace,
		"container": info.Container,
		"command":   info.DefaultCommand,
	}, "执行参数")

	//1. 创建执行命令的request
	execRequest := clientset.CoreV1().RESTClient().Post().
		Resource("pods").
		Name(info.Name).
		Namespace(info.Namespace).
		SubResource("exec").
		VersionedParams(&v1.PodExecOptions{
			Container: info.Container,
			Command:   []string{info.DefaultCommand},
			Stdin:     true,
			Stdout:    true,
			Stderr:    true,
			TTY:       true,
		}, scheme.ParameterCodec)

	logs.Debug(map[string]interface{}{"url": execRequest.URL().String()}, "K8s exec请求URL")

	//2. 创建一个命令执行器，用于执行k8s命令
	//创建一个rest config
	restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "创建REST配置失败")
		info.ReturnData.Message = "创建REST配置失败:" + err.Error()
		info.ReturnData.Status = 400
		r.JSON(http.StatusOK, info.ReturnData)
		return
	}

	executor, err := remotecommand.NewSPDYExecutor(restConfig, "POST", execRequest.URL())
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "创建命令执行器失败")
		info.ReturnData.Message = "创建命令执行器失败:" + err.Error()
		info.ReturnData.Status = 400
		r.JSON(http.StatusOK, info.ReturnData)
		return
	}

	//升级为websocket协议
	ws := CreateWS(r)
	if ws == nil {
		logs.Error(nil, "WebSocket升级失败")
		info.ReturnData.Message = "WebSocket升级失败"
		info.ReturnData.Status = 400
		r.JSON(http.StatusOK, info.ReturnData)
		return
	}
	defer ws.Close()

	//2.1定义一个ptyHandler用于实现streamOptions相关的接口
	pty := &ptyHandler{
		ws:         ws,
		resizeChan: make(chan remotecommand.TerminalSize, 10), // 修复：初始化resizeChan
	}

	// 发送连接成功消息
	welcomeMsg := fmt.Sprintf("\r\n🎉 成功连接到容器 %s\r\n", info.Name)
	if info.DefaultCommand == "/bin/bash" {
		welcomeMsg += "使用bash shell...\r\n"
	} else {
		welcomeMsg += "使用sh shell...\r\n"
	}
	welcomeMsg += "$ "

	err = ws.WriteMessage(websocket.TextMessage, []byte(welcomeMsg))
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "发送欢迎消息失败")
	}

	//2.1定义一个StreamOptions
	streamOptions := remotecommand.StreamOptions{
		Stdin:             pty,
		Stdout:            pty,
		Stderr:            pty,
		Tty:               true,
		TerminalSizeQueue: pty,
	}

	logs.Debug(nil, "开始建立K8s exec流连接")

	//3.创建一个长连接进行实时交互
	err = executor.StreamWithContext(context.TODO(), streamOptions)
	if err != nil {
		logs.Error(map[string]interface{}{"msg": err.Error()}, "命令执行器创建长连接失败")

		// 发送错误信息到前端
		pty.sendError(fmt.Sprintf("无法连接到容器: %s", err.Error()))

		// 如果是bash不存在的错误，建议使用sh
		if info.DefaultCommand == "/bin/bash" {
			pty.sendError("容器可能不支持bash，建议使用sh")
		}

		// 延迟关闭，让错误消息能够发送
		// time.Sleep(time.Second)
	} else {
		logs.Debug(nil, "K8s exec流连接正常结束")
	}
}
